package com.gy.boot.common.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.setting.Setting;
import com.gy.boot.common.exception.SysException;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;

/**
 * 文件上传工具(指定配置文件目录上传)
 * ！！！上传文件应定义好各个业务目录，方便清理临时文件
 * @author geYang
 */
@Component
public class UploadUtil {
    /**
     * 文件指定访问URL
     */
    public static final String BASE_URL = "/files/";
    /**
     * 文件指定存放目录
     */
    public static final String BASE_PATH = getBasePath();

    /**
     * 获取文件基础路径
     * */
    private static String getBasePath() {
        String base_url = BASE_URL; // 文件基础路径-必须以该地址结尾方便页面访问
        String basePath = new Setting("application.properties").getStr("upload.base.path");
        if (BlankUtil.isBlankNull(basePath)) {
            basePath = FileUtil.getWebRoot().getParentFile().getAbsolutePath() + base_url;
        } else {
            basePath = basePath.endsWith(base_url) ? basePath : (basePath + base_url);
        }
        basePath = basePath.replaceAll("//", "/");
        System.out.println("初始化文件上传基础路径BasePath: " + basePath);
        return basePath;
    }

    /** 用户图片存放目录 */
    public static final String USER_IMAGE = "userImage/";
    /** 图片存放目录 */
    public static final String IMAGE = "image/";
	/** 其他文件存放目录 */
    private static final String OTHER = "other/";


    /** 临时文件地址，可定时器清理 */
    public static final String TEMP = "temp/";
    /** 删除临时目录 */
    public static void delTempDir() {
        String tempDir = BASE_PATH + TEMP; // 清除临时目录-获取基础路径
        System.out.println("删除文件目录：" + tempDir);
        FileUtil.del(tempDir);
    }


    /** 允许上传文件集合 */
    private static final String[] ALLOW_FORMAT = {
        ".jpg", ".png", ".gif", ".jpeg", ".txt", ".zip", ".doc", ".docx", ".xls", ".xlsx", ".pdf"
    };

    private static String initPath(String path) {
        return BlankUtil.isBlankNull(path) ? OTHER : (path.endsWith("/") ? path : path+"/");
    }
    /**
     * 获取文件上传绝对路径(E:/Test/boot-demo/files/other/01.jpg)
     * @param path 默认为 OTHER(other)
     * @param fileName 必填文件名称(01.jpg)
     * */
    private static String getUploadFilePath(String path, String fileName) {
        String base_path = BASE_PATH;
    	System.out.println("basePath(上传基础路径): " + base_path);
        path = initPath(path);
        String filePath = base_path + path + fileName;
        filePath = filePath.replace("//", "/");
        System.out.println("filePath(上传绝对路径): " + filePath);
        return filePath;
    }
    /** 获取文件上传访问路径: (/files/other/01.jpg)
     * @param path 默认为 OTHER(other)
     * @param fileName 必填文件名称(01.jpg)
     * */
    private static String getUploadFileUrl(String path, String fileName) {
        path = initPath(path);
        String fileUrl = BASE_URL + path + fileName;
        fileUrl = fileUrl.replace("//", "/");
        System.out.println("fileUrl(上传访问路径): " + fileUrl);
        return fileUrl;
    }

    public static final String FilePath = "filePath"; // 绝对地址Key
    public static final String FileUrl = "fileUrl";  // 访问地址Key
    public static Map<String, String> getUploadFileMap(String path, String fileName) {
        fileName = FileUtil.cleanInvalid(fileName);
        String filePath = getUploadFilePath(path, fileName); // 获取上传绝对地址
        String fileUrl = getUploadFileUrl(path, fileName); // 获取上传访问地址
        Map<String, String> params = new HashMap<>();
        params.put(FilePath, filePath); // 绝对地址
        params.put(FileUrl, fileUrl); // 绝对地址
        return params;
    }

    /** 获取临时文件上传地址 */
    public static Map<String, String> getTempFileMap(String fileName) {
        return getUploadFileMap(TEMP, fileName);
    }

    /**
     * 文件上传
     * @param file 文件
     * @param path 存放目录
     * @author geYang 2018-06-13 07:09
     * */
    public static String upload(MultipartFile file, String path, boolean isReturnPath) throws IOException {
        if (file.isEmpty()) { return null; }
        // 文件存放目录
        String fileName = getFileName(file.getOriginalFilename());
        Map<String, String> fileMap = getUploadFileMap(path, fileName);
        String filePath = fileMap.get(UploadUtil.FilePath);
        FileUtil.writeBytes(file.getBytes(), filePath);
        if (isReturnPath) { return filePath; } // 返回绝对路径
        return fileMap.get(UploadUtil.FileUrl);
    }


    /**
     * 获取文件存放全路径路径(相对路径)
     * @param fileUrl 文件访问路径
     * @return  F:/IMAGE/other/08.jpg
     * */
    private static String getUploadPath(String fileUrl) {
        if (fileUrl.startsWith(BASE_PATH)) { return fileUrl; }
        String uploadPath = fileUrl.replace(BASE_URL, BASE_PATH);
        System.out.println("uploadPath(文件存放路径): " + uploadPath);
        return uploadPath;
    }
    
    /**
     * 生成随机文件名
     * @param originalName 上传文件名
     * @return fileName 随机文件名
     * @author geYang  2018-06-13 10:54
     */
    public static String getFileName(String originalName) {
        String suffix = getSuffix(originalName);
        String fileName = DateTool.format(new Date(), "yyyyMMddHHmmss") + StrTool.randomString(5).toLowerCase() + suffix;
        FileUtil.cleanInvalid(fileName);
        return fileName;
    }

    /**
     * 获取文件后缀名
     */
    public static String getSuffix(String name) {
        String suffix = name.substring(name.lastIndexOf("."));
        if (!checkSuffix(suffix)) {
            throw new SysException("文件不合法");
        }
        return suffix;
    }
    
    /**
     * 判断文件名是否合格
     * @param name 文件名
     * */
    public static boolean checkName(String name) {
        return checkSuffix(getSuffix(name));
    }

    /**
     * 校验后缀名是否合法
     * */
    private static boolean checkSuffix(String suffix) {
        Set<String> setSuffix = new HashSet<>(Arrays.asList(ALLOW_FORMAT));
        return setSuffix.contains(suffix);
    }
    
    /**
     * 获取文件下载数据
     * @author geYang 2018-06-13 10:20
     * */
    private static byte[] getDownloadByte(String fileUrl) {
        String uploadPath = getUploadPath(fileUrl);
        return FileUtil.readBytes(uploadPath);
    }

    /**
     * 文件下载
     * @param fileUrl 文件访问路径
     * @param fileName 定义下载文件名,不带后缀
     * */
    public static void download(String fileUrl, String fileName, HttpServletResponse response) throws IOException {
        fileName = fileName.trim();
        boolean isHttp = fileUrl.startsWith("http://") || fileUrl.startsWith("https://");
        fileUrl = isHttp ? waterFile(fileUrl, fileName) : fileUrl;
        byte[] downloadByte = getDownloadByte(fileUrl);
        if (downloadByte == null) {
            throw new SysException("下载文件不存在");
        }
        String suffix = getSuffix(fileUrl); // 文件后缀
        fileName = fileName.endsWith(suffix) ? fileName : (fileName + suffix);
        fileName = FileNameUtil.cleanInvalid(URLEncoder.encode(fileName, "UTF-8"));
        response.reset();
        response.setCharacterEncoding("utf-8");
        response.setContentType("multipart/form-data");
        response.setContentType("application/octet-stream; charset=UTF-8");
        response.addHeader("Access-Control-Allow-Origin", "*"); // 解决跨域
        response.setHeader("Content-Disposition", "attachment; filename=\""+ fileName  +"\"");
        response.addHeader("Content-Length", String.valueOf(downloadByte.length));
        IoUtil.write(response.getOutputStream(), true, downloadByte);
    }

    /** 下载文件到本地 */
    private static String waterFile(String fileUrl, String fileName) throws IOException {
        String filePath = getUploadFilePath(null, fileName);// 文件下载地址
        FileUtil.del(filePath); // 删除原文件
        // 创建新文件
        URL url = URLUtil.url(fileUrl);
        FileUtil.writeFromStream(url.openStream(), filePath);
        return filePath;
    }

}
